iT邦幫忙

2022 iThome 鐵人賽

DAY 10
0
自我挑戰組

Spring In Action系列 第 10

Configuration

  • 分享至 

  • xImage
  •  

說到框架,就肯定無法避開configuration的議題,這篇將會來探討Spring有哪些default configuration以及如何設定custom configuration。

說到Spring的configuration,第一直覺一定是想到application.properties檔或者application.yaml檔,這邊也是主要來談這個方式的configuration,不過其實不只透過application.properties才能進行config,諸如JVM, OS env var, cmd-line args等等也都可以是SpringBootApplication能吃進來的config properties。SpringBoot會將這些來源全都透過抽象化的介面接起來,並實際注入到Spring application context的instances之中。

以下為設定tomcat server port的範例:

  • application.properties || application.yaml
#application.properties
server.port = 8081

#application.yaml
server:
  port: 8081
  • JVM system properties
$ java -jar sample-0.0.5-SNAPSHOT.jar --server.port=8081
  • operating system environment variable
$ export SERVER_PORT=8081

先前有提到,我們可以放schema.sql以及data.sql在src/main/resources,Spring application啟動的時候會自動執行這兩個sql;但若不只有這兩個sql想在啟動時執行呢?可進行以下設定:

spring:
  datasource:
    schema:
    - order-schema.sql
    - ingredient-schema.sql
    data:
    - ingredients.sql

或可透過JNDI的方式來讀取:

spring:
  datasource:
    jndi-name: java:/comp/env/jdbc/demoAppDS

再來是介紹SSL的設定:

server:
  port: 8443
  ssl:
    key-store: file://path/to/mykeys.jks
    key-store-password: dummypwd
    key-password: dummypwd

常常也會需要設定logging,SpringBoot預設是使用Logback來進行log詳細的設定,我們可以在src/main/resources中建立logback.xml來進行細部的設定。

不過若只是要設定level和將log產生在哪裡,可以直接透過application.properties來進行:

logging:
  level:
   root: WARN
   org.springframework.security: DEBUG
  file:
    path: /var/logs
    file: DemoApp.log

預設當log file達到10mb的時候就會rotate紀錄

在application.properties中也可以引用其他的properties當作變數來設定值:

greeting:
  welcome: You are using ${spring.application.name}

除了預設可以設定的屬性外,我們還可以自定義屬性,並在java程式中引入這些appication.properties設定的屬性值進來,而要做到這點就是透過@ConfigurationProperties。

#application.yaml
demo:
  orders:
    pageSize: 10
@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
@ConfigurationProperties(prefix="demo.orders")
public class OrderController {
 
  private int pageSize = 20;
 
  public void setPageSize(int pageSize) {
    this.pageSize = pageSize;
  }
 
  ...
  @GetMapping
  public String ordersForUser(
      @AuthenticationPrincipal User user, Model model) {
 
    Pageable pageable = PageRequest.of(0, pageSize);
    model.addAttribute("orders",
        orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
    return "orderList";
  }
}

除了直接注入到所需的class中,我們可以將這些properties分隔出來管理:

package demo.web;
import org.springframework.boot.context.properties.
                                        ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
 
@Component
@ConfigurationProperties(prefix="demo.orders")
@Data
public class OrderProps {
  
  private int pageSize = 20;
 
}
private OrderProps props;
 
public OrderController(OrderRepository orderRepo,
        OrderProps props) {
  this.orderRepo = orderRepo;
  this.props = props;
}
 
  ...
 
@GetMapping
public String ordersForUser(
    @AuthenticationPrincipal User user, Model model) {
 
  Pageable pageable = PageRequest.of(0, props.getPageSize());
  model.addAttribute("orders",
      orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
 
  return "orderList";
}

這樣不論是java程式的屬性設定,還是@ConfigurationProperties的設定都可以集中在Props類別中管理。

而我們自定義的properties在apppication.properties會被IDE警告說這是未定義的properties,我們可以新增src/main/resources/META-INF/additional-spring-configuration-metadata.json

{"properties": [{
  "name": "demo.orders.page-size",
  "type": "java.lang.String",
  "description": "A description for 'demo.orders.page-size'"
}]}

以上是透過Spring Suite Tool IDE功能自動生出來的,可能會不太準確,不過格式就是長這樣,當我們有設定這個後,在application.properties就不會有warning,而且當我們把鼠標移到properties上時,還會顯示出我們剛剛定義的內容變成說明。

我們在開發系統時,一定會有很多個環境的問題,不同的環境就可能會有不同的appication.properties,這時該怎麼辦呢?大致在SpringBoot有兩種方法。

一種是直接在檔名作區別:application-{env}.properties,不過運作規則不是只讀取這個有標註env的properties檔,而是會先讀取application.properties,然後再參考application-{env}.properties,看是否有多的屬性,或者重複的屬性會override。

另一種是在同一份application.properties裡面做區隔:

logging:
  level:
    tacos: DEBUG
 
---
spring:
  profiles: prod
 
  datasource:
    url: jdbc:mysql:/ /localhost/tacocloud
    username: tacouser
    password: tacopassword
 
logging:
  level:
    tacos: WARN

不同env的設定會透過”- - -”分隔,並會有spring.profiles=prod標註是哪個env。
然後在機器上執行的時候打上:

% java -jar taco-cloud.jar --spring.profiles.active=prod

以上代表要使用prod的application-prod.properties,並會先讀取application.properties,若有重複的屬性會使用prod的進行override。

@Bean也可以設定只被哪個profile取用:

@Bean
@Profile("dev")
public CommandLineRunner dataLoader(IngredientRepository repo,
      UserRepository userRepo, PasswordEncoder encoder) {
 
  ...
 
}

甚至還可以用"!”

@Bean
@Profile("!prod")
public CommandLineRunner dataLoader(IngredientRepository repo,
      UserRepository userRepo, PasswordEncoder encoder) {
 
  ...
 
}

上一篇
Spring Security
下一篇
RESTful API in Spring & Spring Data REST
系列文
Spring In Action30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言